import { proto } from "./array";
import Dep from "./dep"

class Observer {
  constructor ( value ) {
    // 相当于给每个对象新增一个dep属性，让对象本身能够触发依赖收集(主要是解决数组更新问题)
    // 因为数组无法被Object.defineProperty拦截，数组是通过重写函数的方式，当数据发生变化后，通过重写进行拦截，并且让数组本身触发dep通知
    this.dep = new Dep()
    // 让对象本身，挂载observer实例
    // 主要的目的数组新增时，能够调用对象本身的dep触发通知更新，同时也能够调用observeArray让新增的对象再次代理
    Object.defineProperty( value, "__ob__", {
      enumerable: false, // 遍历对象的时候跳过，防止死循环
      value: this,
    } );
    if ( Array.isArray( value ) ) {
      // 重写数组的七个方法
      value.__proto__ = proto;
      // 如果数组里有对象，需要再次代理
      this.observeArray( value );
    } else {
      // 对象
      this.walk( value )
    }
  }

  walk ( target ) {
    Object.keys( target ).forEach( ( key ) => {
      this.defineReactive( target, key, target[ key ] )
    } )
  }

  observeArray ( target ) {
    for ( let index = 0; index < target.length; index++ ) {
      observe( target[ index ] )
    }
  }

  dependArray ( value ) {
    for ( let i = 0; i < value.length; i++ ) {
      let c = value[ i ]; // [[[[[[[[]]]]]]]]
      c.__ob__ && c.__ob__.dep.depend(); // 让数组中的对象或者数组再次依赖收集  [{name:'zzz'},[]]
      if ( Array.isArray( c ) ) {
        this.dependArray( c ); // 保证数组中的对象和数组都能有依赖收集的功能
      }
    }
  }

  defineReactive ( target, key, value ) {
    // 每一个对象的属性都会关联一个dep实例
    const dep = new Dep()
    // 全量递归，性能差
    let childOb = observe( value )
    Object.defineProperty( target, key, {
      get: () => {

        // 在取值的时候，如果有watcher(渲染watcher、计算属性watcher、监听watcher)
        // 就会触发依赖收集
        if ( Dep.target ) {
          dep.depend()
          // 如果对象中有数组，让数组本身也依赖收集
          // 主要是解决数组更新问题，数组不会被拦截
          if ( childOb ) {
            childOb.dep.depend()
            // 
            if ( Array.isArray( value ) ) {
              this.dependArray( value )
            }
          }
        }
        return value
      },
      set: ( newValue ) => {
        if ( newValue !== value ) {
          // 如果设置的值是对象，继续需要代理
          childOb = observe( newValue )
          value = newValue
          // 当属性值发生变化后，需要通知watcher更新
          dep.notify()
        }
      }
    } )
  }
}


export function observe ( data ) {
  // 只能处理对象
  if ( typeof data !== "object" || data == null ) {
    return;
  }
  // 代理过都有__ob__属性，不需要重复代理
  if ( data.__ob__ ) return;

  return new Observer( data );
}
